package gu.sql2java.store;

import static gu.sql2java.store.BinaryUtils.saveBytes;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;

/**
 * 二进制数据本地存储实现
 * @author guyadong
 *
 */
public class LocalBinaryStore extends BaseURLStore {
	private static final String ORIGIN_ROOT_NAME = "origin";
	private static final String PROTOCOL = "lbs";
	private final URLStreamHandler handler = new Handler(); 
	private File storeRoot;
	public static final LocalBinaryStore SINGLETON = new LocalBinaryStore();
	private LocalBinaryStore() {
	}

	/**
	 * 创建iadb存储地址对象(将path中storeRoot路径剥离)
	 * @param file
	 * @return iadb存储地址URL
	 */
	protected URL createStoreURL(File file){
		try {
			return new URL(PROTOCOL, null,file.toURI().toURL().getPath().substring(getStoreRoot().toURI().toURL().getPath().length()));
		} catch (MalformedURLException e) {
			throw new RuntimeException(e);
		}
	}
	@Override
	protected boolean doExists(URL storedURL){
		return pathOf(storedURL).canRead();
	}
	@Override
	protected URL doFind(final String md5) {
		File folder = relativeFolderPath(md5);
		if(folder.isDirectory()){
			File[] files = folder.listFiles(new FilenameFilter() {
				@Override
				public boolean accept(File dir, String name) {
					return name.startsWith(md5);
				}
			});
			try {
				return files.length >0 ? files[0].toURI().toURL() : null;
			} catch (MalformedURLException e) {
				throw new RuntimeException(e);
			}
		}
		return null;
	}

	protected File pathOf(URL storedURL) {
		// 在path中添加root路径
		return new File(getStoreRoot().getPath(),storedURL.getPath());
	}
	@Override
	protected URL doStore(byte[] binary, String md5, String extension, boolean makeURLOnly) throws IOException {
		File dst = createDestFile(md5, extension);
		if(!makeURLOnly){
			if(dst.length() != binary.length){
				saveBytes(binary, dst, true);
			}			
		}
		return createStoreURL(dst);
	}
	@Override
	protected boolean doDelete(URL storedURL) throws IOException{
		File p = pathOf(storedURL);
		if(p.exists()){
			return p.delete();
		}
		return false;
	}

	@Override
	public final String getProtocol() {
		return PROTOCOL;
	}

	public LocalBinaryStore setStoreRoot(File storeRoot) {
		this.storeRoot = storeRoot;
		return this;
	}

	private File getStoreRoot() {
		return ConditionChecks.checkNotNull(storeRoot, 
				IllegalStateException.class, 
				"storeRoot is uninitialized,please call setStoreRoot(File) firstly");
	}

	protected String relativeFilePath(String md5, String suffix){
		// md5前两位做第一级子目录，接下来的两位做第二级子目录
		String s = (suffix == null || suffix.length() == 0) ? "" : "." + suffix;
		File f = new File(new File(new File(ORIGIN_ROOT_NAME, md5.substring(0, 2)),md5.substring(2, 4)),md5 + s);
		return  f.toString();
	}
	protected File relativeFolderPath(String md5){
		// md5前两位做第一级子目录，接下来的两位做第二级子目录
		return new File(new File(ORIGIN_ROOT_NAME, md5.substring(0, 2)),md5.substring(2, 4));
	}
	private File createDestFile(String md5, String suffix){
		return new File(getStoreRoot(),relativeFilePath(md5, suffix));
	}

	@Override
	protected URLStreamHandler doGetURLStreamHandler() {
		return handler;
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = super.hashCode();
		result = prime * result + ((getProtocol() == null) ? 0 : getProtocol().hashCode());
		result = prime * result + ((storeRoot == null) ? 0 : storeRoot.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (!super.equals(obj))
			return false;
		if (!(obj instanceof LocalBinaryStore))
			return false;
		LocalBinaryStore other = (LocalBinaryStore) obj;
		if (getProtocol() == null) {
			if (other.getProtocol() != null)
				return false;
		} else if (!getProtocol().equals(other.getProtocol()))
			return false;
		if (storeRoot == null) {
			if (other.storeRoot != null)
				return false;
		} else if (!storeRoot.equals(other.storeRoot))
			return false;
		return true;
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("LocalBinaryStore [protocol=");
		builder.append(getProtocol());
		builder.append(",storeRoot=");
		builder.append(storeRoot);
		builder.append("]");
		return builder.toString();
	}

	private class Handler extends URLStreamHandler {
		@Override
		protected URLConnection openConnection(URL u) throws IOException {
			return new FileConnection(u);
		}
	}
	
	private class FileConnection extends URLConnection{

		protected FileConnection(URL url) {
			super(url);
		}

		@Override
		public void connect() throws IOException {
			connected = true;
		}

		@Override
		public InputStream getInputStream() throws IOException {
			connect();
			try {
				return new FileInputStream(pathOf(url));
			} catch (FileNotFoundException e) {
				throw new DataNotFoundException(url,e);
			}
		}
		
	}
}
